<?php
/**
 * 本class主要在分库分表时使用
 * 用于决定data层链接数据库时，使用什么样的配置信息，哪个数据库表
 */

/// 类定义开始
class DbDispatcher {

	/**
	 * 设置真实的数据库表
	 *
	 * @param string table_alias, 表别名
	 * @param string hash_key, 表的分库分表规则依据的key
	 * @param string mode r/w, 读/写，主从可能需要用到
	 */
    public static function dispatchTable($table_alias, $hash_key='', $mode='w') {

    	if (empty($table_alias)) return false;

		/* 每个表的分发规则定义在一个函数里，函数命名规则：_表别名_dispatch() */
		$table_func = '_db_'.$table_alias.'_dispatch';

		$db_cfg = C('__DB_CONFIG');

		if (!isset($db_cfg[$table_alias])) {
			trigger_error(__FUNCTION__.'db配置文件里没有定义表['.$table_alias.']的数据库连接配置信息', E_USER_ERROR);
		}
		$table_cfg=$db_cfg[$table_alias];
		if(!empty($table_cfg['R_SERVERS'])&& $mode=='r'){
			$count=count($table_cfg['R_SERVERS']);
			$i=rand(1,$count);
			$server=$table_cfg['R_SERVERS'][$i-1];
			$table_cfg['SERVER']=$server;
		}
		if (function_exists($table_func)) {

			$ret = $table_func($table_cfg, $table_alias, $hash_key, $mode);
			if (!is_array($ret)) {
				trigger_error(__FUNCTION__.'尝试调用方法'.$table_func.', 但返回值不正确，请检查db_dispatcher.cfg.php ', E_USER_ERROR);
			}

			return $ret;
		}

		/// 判断db_cfg的合法性
		/// 不定义$table_alias的分发规则的话，采用默认分发规则
		if (isset($table_cfg['SERVER']) && isset($table_cfg['USERNAME']) && isset($table_cfg['PASSWORD']) && isset($table_cfg['DBTABLE'])) {
			return DbDispatcher::hashDispatch($table_cfg, $hash_key, $mode);
		}

		trigger_error(__FUNCTION__.' 您没有定义'.$table_alias.'的dispatch规则，db config里的定义不合法', E_USER_ERROR);
    }

    /**
     * 默认分发规则
     * 采用hash散列，例如需要10个库(10相当于是个基数，将来多于10个库，这个参数还是10)，每个库100张表，每张表100万数据
     * 则预计初期支撑的数据规模是：10*100*100万=10亿
     * 数据库名是：
     * 	db2_user_$m_$n
     * 	$m = floor($hash_key/10亿)
     * 	$n = floor($hash_key%10)
     * 表名是：
     * 	t_user_$k
     * 	$k = ((floor($hash_key/10))%100)
     */
    public static function hashDispatch($cfg, $hash_key='', $mode='w') {

    	if ($hash_key === '') return $cfg;
    	if (!(isset($cfg['DISPATCH']) && is_array($cfg['DISPATCH']))) {
    		return $cfg;
    	}


    	$db_ary = explode('.', $cfg['DBTABLE']);

    	if ($cfg['DISPATCH'][1] === false) {
    		$m = 0;
    		$n = 0;
    		$k = 0;

    		$table_name = $db_ary[1];
    	}
    	else {
	    	$m = floor(bcdiv($hash_key, ($cfg['DISPATCH'][1]*$cfg['DISPATCH'][2]*$cfg['DISPATCH'][3]), 2));
	    	$n = bcmod($hash_key, $cfg['DISPATCH'][1]);
	    	$k = bcmod(floor(bcdiv($hash_key, $cfg['DISPATCH'][1])), $cfg['DISPATCH'][2]);

	    	$table_name = $db_ary[1].'_'.$k;
    	}

    	$db_name    = $db_ary[0].'_'.$m.'_'.$n;

    	$cfg['DBTABLE'] = $db_name.'.'.$table_name;

    	return $cfg;
    }

    public static function loadSql($sql_file) {

    	$sql_content = Factory::getSql($sql_file, '');

    	return $sql_content;
    }
}